package org.heng.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.heng.dao.entity.SeatDO;
import org.heng.dao.entity.TicketDO;
import org.heng.dao.entity.UserTicketDO;
import org.heng.dao.mapper.SeatMapper;
import org.heng.dao.mapper.TicketMapper;
import org.heng.dto.SeatDetailsDTO;
import org.heng.dto.req.*;
import org.heng.dto.resp.AvaliableSeatRespDTO;
import org.heng.dto.resp.TicketRespDTO;
import org.heng.mq.entity.CreateOrderEvent;
import org.heng.mq.entity.DelayCloseOrderEvent;
//import org.heng.mq.producer.AbstractMessageProducer;
//import org.heng.mq.producer.CreateOrderMessageProducer;
import org.heng.service.SeatService;
import org.heng.service.TicketService;
import org.heng.service.UserTicketService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class TicketServiceImpl implements TicketService {
    private final TicketMapper ticketMapper;
//    private final CreateOrderMessageProducer createOrderMessageProducer;
    private final SeatService seatService;
    private final SeatMapper seatMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final RedissonClient redissonClient;
    private final UserTicketService userTicketService;
    private final TransactionTemplate transactionTemplate;

    private static final String PREFIX_LOCK = "sob:lock:";
    private static final String PREFIX_TICKET = "sob:ticket:";
    private static final String PREFIX_TICKET_NO_STOCK = "sob:no:ticket:";
    private static final String PREFIX_SEAT = "sob:seat:";
    private static final String PRIFIX_TICKET_STOCK = "sob:stock:ticket:";

    @Override
    public void purchaseTicket(BuyTicketReqDTO requestParams) {
        RLock lock = redissonClient.getLock(PREFIX_LOCK + requestParams.getTicketName() + requestParams.getRegion());

        boolean tryLock = lock.tryLock();
        if (!tryLock) {
            throw new RuntimeException("正在购票中，请稍等...");
        }
        try {
            TicketRespDTO ticketRespDTO = queryTicketsByName(requestParams.getTicketName());
            SeatDetailsDTO seatDetailsDTO = ticketRespDTO.getSeatDetailsDTOList().stream().filter(each -> each.getRegion().equals(requestParams.getRegion())).findFirst().get();
            String cacheKey = PREFIX_SEAT + requestParams.getTicketName() + requestParams.getRegion();

            Set<String> set = stringRedisTemplate.opsForZSet().reverseRange(cacheKey, 0, -1);
            if (set == null) throw new RuntimeException("购票失败");
            List<Integer> seat = new ArrayList<>();
            for (String cnt : set) {
                int c = Integer.parseInt(cnt);
                if (seat.isEmpty() || seat.get(seat.size() - 1) == c + 1) {
                    seat.add(c);
                } else {
                    seat.clear();
                }
                if (seat.size() == requestParams.getCount()) break;
            }
            if (seat.size() < requestParams.getCount()) throw new RuntimeException("购票失败");
            List<String> collect = seat.stream().map(Object::toString).collect(Collectors.toList());
            stringRedisTemplate.executePipelined((RedisCallback<?>) connection -> {
                collect.forEach(each -> connection.zSetCommands().zRem(cacheKey.getBytes(), each.getBytes()));
                return null;
            });
            transactionTemplate.executeWithoutResult(status -> {
                try {
                    Integer i = seatMapper.selectSeat(requestParams.getTicketName(), collect);
                    // 创建用户购票记录
                    List<UserTicketDO> userTicketDOList = new ArrayList<>();
                    seat.forEach(each -> {
                        UserTicketDO userTicketDO = new UserTicketDO();
                        userTicketDO.setCode(each.toString());
                        userTicketDO.setName(requestParams.getTicketName());
                        userTicketDO.setUserId(1926856517563125761L);
                        userTicketDO.setRegion(requestParams.getRegion());
                        userTicketDO.setPrice(seatDetailsDTO.getPrice());
                        userTicketDO.setSId(ticketRespDTO.getId());
                        userTicketDO.setStatus(0);
                        userTicketDOList.add(userTicketDO);
                    });
                    userTicketService.saveBatch(userTicketDOList);
                    if (!SqlHelper.retBool(i)) {
                        stringRedisTemplate.opsForValue().set(PREFIX_TICKET_NO_STOCK + requestParams.getTicketName(),"");
                        throw new RuntimeException("购票失败");
                    }
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw new RuntimeException("购票失败");
                }
            });
            // 创建订单
            CreateOrderEvent createOrderEvent = new CreateOrderEvent();
            createOrderEvent.setName(requestParams.getTicketName());
            createOrderEvent.setRegion(requestParams.getRegion());
            createOrderEvent.setCodeList(seat.stream().map(Object::toString).collect(Collectors.toList()));
//            SendResult sendResult = createOrderMessageProducer.sendMessage(createOrderEvent);

//            if (!sendResult.getSendStatus().name().equals("SEND_OK")) {
//                log.warn("创建订单失败:{}", JSON.toJSONString(createOrderEvent));
//            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public TicketRespDTO createTicket(CreateTicketReqDTO requestParams) {
        TicketDO ticketDO = BeanUtil.toBean(requestParams, TicketDO.class);
        RLock lock = redissonClient.getLock( PREFIX_LOCK+ requestParams.getName());

        boolean tryLock = lock.tryLock();
        if (!tryLock) throw new RuntimeException("正在创建中...");
        try {
            ticketMapper.insert(ticketDO);

            CreateSeatReqDTO createSeatReqDTO = BeanUtil.toBean(requestParams, CreateSeatReqDTO.class);
            seatService.createSeat(createSeatReqDTO);
            Map<String, Object> stringObjectMap = BeanUtil.beanToMap(ticketDO);
            // 将 Map集合中的value 转换为 string类型
            Map<String, String> map = stringObjectMap.entrySet()
                    .stream().collect(Collectors.toMap(
                            Map.Entry::getKey,
                            entry -> entry.getValue() != null ? entry.getValue().toString() : "")
                    );
            stringRedisTemplate.opsForHash().putAll(PREFIX_TICKET + requestParams.getName(), map);

            List<SeatDetailsDTO> seatDetailsDTOList = createSeatReqDTO.getSeatDetailsDTOList();
            seatDetailsDTOList.forEach(each -> {
                Map<String, String> map1 = new HashMap<>();
                map1.put("count",each.getCount().toString());
                map1.put("price",each.getPrice().toString());
                stringRedisTemplate.opsForHash().putAll(PRIFIX_TICKET_STOCK + requestParams.getName() + each.getRegion(), map1);
            });
        }catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException("插入票数据失败");
        }finally {
            lock.unlock();
        }
        TicketRespDTO ticketRespDTO = BeanUtil.toBean(ticketDO, TicketRespDTO.class);

        QueryAvaliableSeatReqDTO queryAvaliableSeatReqDTO = BeanUtil.toBean(requestParams, QueryAvaliableSeatReqDTO.class);
        AvaliableSeatRespDTO avaliableSeatRespDTO = seatService.queryAvaliableSeat(queryAvaliableSeatReqDTO);
        ticketRespDTO.setStock(avaliableSeatRespDTO.getAvaliableSeatCount());
        ticketRespDTO.setSeatDetailsDTOList(avaliableSeatRespDTO.getSeatDetailsDTOList());
        return ticketRespDTO;
    }

    @Override
    public TicketRespDTO queryTicketsById(Long id) {
        TicketDO ticketDO = ticketMapper.selectById(id);
        return BeanUtil.toBean(ticketDO, TicketRespDTO.class);
    }

    @Override
    public TicketRespDTO queryTicketsByName(String name) {
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(PREFIX_TICKET + name);
        TicketDO ticketDO;
        TicketRespDTO ticketRespDTO;
        if (entries.isEmpty()) {
            Wrapper<TicketDO> queryWrapper = Wrappers.lambdaQuery(TicketDO.class).eq(TicketDO::getName, name);
            ticketDO = ticketMapper.selectOne(queryWrapper);
            if (ticketDO == null) {
                throw new RuntimeException("演唱会门票不存在");
            }
            entries = BeanUtil.beanToMap(ticketDO).entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            Map<String, String> map = entries.entrySet().stream()
                    .collect(Collectors.toMap(entry -> entry.getKey().toString(),
                            entry -> entry.getValue() != null ? entry.getValue().toString() : ""));

            stringRedisTemplate.opsForHash().putAll(PREFIX_TICKET + name, map);
        }
        ticketRespDTO = BeanUtil.toBean(entries, TicketRespDTO.class);

        QueryAvaliableSeatReqDTO queryAvaliableSeatReqDTO = new QueryAvaliableSeatReqDTO();
        queryAvaliableSeatReqDTO.setName(name);

        AvaliableSeatRespDTO avaliableSeatRespDTO = seatService.queryAvaliableSeat(queryAvaliableSeatReqDTO);
        ticketRespDTO.setStock(avaliableSeatRespDTO.getAvaliableSeatCount());
        ticketRespDTO.setSeatDetailsDTOList(avaliableSeatRespDTO.getSeatDetailsDTOList());
        return ticketRespDTO;
    }

    @Override
    public void updateStock(TicketReqDTO requestParams) {
//        stringRedisTemplate.delete(PREFIX_TICKET + requestParams.getName());
    }

    @Override
    public void updatePrice(UpdateTicketDTO requestParams) {
        Wrapper<SeatDO> queryWrapper = Wrappers.lambdaQuery(SeatDO.class)
                .eq(SeatDO::getName, requestParams.getName())
                .eq(SeatDO::getRegion, requestParams.getRegion());

        SeatDO seatDO = seatMapper.selectOne(queryWrapper);
        seatDO.setPrice(requestParams.getPrice());
        seatMapper.update(seatDO, queryWrapper);
//        stringRedisTemplate.delete(PREFIX_TICKET + requestParams.getName());
    }
}
